<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------

/**
 * ThinkPHP Action控制器基类 抽象类
 * @category   Think
 * @package  Think
 * @subpackage  Core
 * @author   liu21st <liu21st@gmail.com>
 */
abstract class Action
{

    /**
     * 视图实例对象
     * @var view
     * @access protected
     */
    protected $view = null;

    /**
     * 当前控制器名称
     * @var name
     * @access protected
     */
    private $name = '';

    /**
     * 模板变量
     * @var tVar
     * @access protected
     */
    protected $tVar = array();

    /**
     * 控制器参数
     * @var config
     * @access protected
     */
    protected $config = array();

    /**
     * 架构函数 取得模板对象实例
     * @access public
     */
    public function __construct()
    {
        tag('action_begin', $this->config);
        //控制器初始化
        if (method_exists($this, '_initialize')) {
            $this->_initialize();
        }
    }

    /**
     * 获取当前Action名称
     * @access protected
     */
    protected function getActionName()
    {
        if (empty($this->name)) {
            // 获取Action名称
            $this->name = substr(get_class($this), 0, -6); // get_class返回对象的类名
            // $this->name =substr(backendAction, 0, -6)= backend;
        }
        return $this->name;  // return backend
    }

    /**
     * 是否AJAX请求
     * @access protected
     * @return bool
     */
    protected function isAjax()
    {
        if (isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
            if ('xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])) {
                return true;
            }
        }
        if (!empty($_POST[C('VAR_AJAX_SUBMIT')]) || !empty($_GET[C('VAR_AJAX_SUBMIT')])) // 判断Ajax方式提交
        {
            return true;
        }
        return false;
    }

    /**
     * 模板显示 调用内置的模板引擎显示方法，
     * @access protected
     * @param string $templateFile 指定要调用的模板文件
     * 默认为空 由系统自动定位模板文件
     * @param string $charset 输出编码
     * @param string $contentType 输出类型
     * @param string $content 输出内容
     * @return void
     */
    protected function display($templateFile = '', $charset = '', $contentType = '', $content = '')
    {
        $this->initView();
        $this->view->display($templateFile, $charset, $contentType, $content);
    }

    /**
     * 输出内容文本可以包括Html 并支持内容解析
     * @access protected
     * @param string $content 输出内容
     * @param string $charset 模板输出字符集
     * @param string $contentType 输出类型
     * @return mixed
     */
    protected function show($content, $charset = '', $contentType = '')
    {
        $this->initView();
        $this->view->display('', $charset, $contentType, $content);
    }

    /**
     *  获取输出页面内容
     * 调用内置的模板引擎fetch方法，
     * @access protected
     * @param string $templateFile 指定要调用的模板文件
     * 默认为空 由系统自动定位模板文件
     * @return string
     */
    protected function fetch($templateFile = '')
    {
        $this->initView();
        return $this->view->fetch($templateFile);
    }

    /**
     * 初始化视图
     * @access private
     * @return void
     */
    private function initView()
    {
        //实例化视图类
        if (!$this->view) {
            $this->view = Think::instance('View');
        }
        // 模板变量传值
        if ($this->tVar) {
            $this->view->assign($this->tVar);
        }
    }

    /**
     *  创建静态页面
     * @access protected
     * @htmlfile 生成的静态文件名称
     * @htmlpath 生成的静态文件路径
     * @param string $templateFile 指定要调用的模板文件
     * 默认为空 由系统自动定位模板文件
     * @return string
     */
    protected function buildHtml($htmlfile = '', $htmlpath = '', $templateFile = '')
    {
        $content  = $this->fetch($templateFile);
        $htmlpath = !empty($htmlpath) ? $htmlpath : HTML_PATH;
        $htmlfile = $htmlpath . $htmlfile . C('HTML_FILE_SUFFIX');
        if (!is_dir(dirname($htmlfile))) // 如果静态目录不存在 则创建
        {
            mkdir(dirname($htmlfile), 0777, true);
        }
        if (false === file_put_contents($htmlfile, $content)) {
            throw_exception(L('_CACHE_WRITE_ERROR_') . ':' . $htmlfile);
        }
        return $content;
    }

    /**
     * 模板变量赋值
     * @access protected
     * @param mixed $name 要显示的模板变量
     * @param mixed $value 变量的值
     * @return void
     */
    protected function assign($name, $value = '')
    {
        if (is_array($name)) {
            $this->tVar = array_merge($this->tVar, $name);
        }
        else {
            $this->tVar[$name] = $value;
        }
    }

    public function __set($name, $value)
    {
        $this->assign($name, $value);
    }

    /**
     * 取得模板显示变量的值
     * @access protected
     * @param string $name 模板显示变量
     * @return mixed
     */
    public function get($name = '')
    {
        if ('' === $name) {
            return $this->tVar;
        }
        return isset($this->tVar[$name]) ? $this->tVar[$name] : false;
    }

    public function __get($name)
    {
        return $this->get($name);
    }

    /**
     * 魔术方法 有不存在的操作的时候执行
     * @access public
     * @param string $method 方法名
     * @param array $args 参数
     * @return mixed
     */
    public function __call($method, $args)
    {
        if (0 === strcasecmp($method, ACTION_NAME . C('ACTION_SUFFIX'))) {
            if (method_exists($this, '_empty')) {
                // 如果定义了_empty操作 则调用
                $this->_empty($method, $args);
            }
            elseif (file_exists_case(C('TEMPLATE_NAME'))) {
                // 检查是否存在默认模版 如果有直接输出模版
                $this->display();
            }
            elseif (function_exists('__hack_action')) {
                // hack 方式定义扩展操作
                __hack_action();
            }
            else {
                _404(L('_ERROR_ACTION_') . ':' . ACTION_NAME);
            }
        }
        else {
            switch (strtolower($method)) {
                // 判断提交方式
                case 'ispost'   :
                case 'isget'    :
                case 'ishead'   :
                case 'isdelete' :
                case 'isput'    :
                    return strtolower($_SERVER['REQUEST_METHOD']) == strtolower(substr($method, 2));
                // 获取变量 支持过滤和默认值 调用方式 $this->_post($key,$filter,$default);
                case '_get'     :
                    $input =& $_GET;
                    break;
                case '_post'    :
                    $input =& $_POST;
                    break;
                case '_put'     :
                    parse_str(file_get_contents('php://input'), $input);
                    break;
                case '_param'   :
                    switch ($_SERVER['REQUEST_METHOD']) {
                        case 'POST':
                            $input = $_POST;
                            break;
                        case 'PUT':
                            parse_str(file_get_contents('php://input'), $input);
                            break;
                        default:
                            $input = $_GET;
                    }
                    if (C('VAR_URL_PARAMS')) {
                        $params = $_GET[C('VAR_URL_PARAMS')];
                        $input  = array_merge($input, $params);
                    }
                    break;
                case '_request' :
                    $input =& $_REQUEST;
                    break;
                case '_session' :
                    $input =& $_SESSION;
                    break;
                case '_cookie'  :
                    $input =& $_COOKIE;
                    break;
                case '_server'  :
                    $input =& $_SERVER;
                    break;
                case '_globals' :
                    $input =& $GLOBALS;
                    break;
                default:
                    throw_exception(__CLASS__ . ':' . $method . L('_METHOD_NOT_EXIST_'));
            }
            if (!isset($args[0])) { // 获取全局变量
                $data = $input; // 由VAR_FILTERS配置进行过滤
            }
            elseif (isset($input[$args[0]])) { // 取值操作
                $data    = $input[$args[0]];
                $filters = isset($args[1]) ? $args[1] : C('DEFAULT_FILTER');
                if ($filters) { // 2012/3/23 增加多方法过滤支持
                    $filters = explode(',', $filters);
                    foreach ($filters as $filter) {
                        if (function_exists($filter)) {
                            $data = is_array($data) ? array_map($filter, $data) : $filter($data); // 参数过滤
                        }
                    }
                }
            }
            else { // 变量默认值
                $data = isset($args[2]) ? $args[2] : NULL;
            }
            return $data;
        }
    }

    /**
     * 操作错误跳转的快捷方法
     * @access protected
     * @param string $message 错误信息
     * @param string $jumpUrl 页面跳转地址
     * @param Boolean|array $ajax 是否为Ajax方式
     * @return void
     */
    protected function error($message, $jumpUrl = '', $ajax = false)
    {
        $this->dispatchJump($message, 0, $jumpUrl, $ajax);
    }

    /**
     * 操作成功跳转的快捷方法
     * @access protected
     * @param string $message 提示信息
     * @param string $jumpUrl 页面跳转地址
     * @param Boolean|array $ajax 是否为Ajax方式
     * @return void
     */
    protected function success($message, $jumpUrl = '', $ajax = false)
    {
        $this->dispatchJump($message, 1, $jumpUrl, $ajax);
    }

    /**
     * Ajax方式返回数据到客户端
     * @access protected
     * @param mixed $data 要返回的数据
     * @param String $type AJAX返回数据格式
     * @return void
     */
    protected function ajaxReturn($data, $type = '')
    {
        if (func_num_args() > 2) { // 兼容3.0之前用法
            $args = func_get_args();
            array_shift($args);
            $info           = array();
            $info['data']   = $data;
            $info['info']   = array_shift($args);
            $info['status'] = array_shift($args);
            $data           = $info;
            $type           = $args ? array_shift($args) : '';
        }
        if (empty($type)) {
            $type = C('DEFAULT_AJAX_RETURN');
        }
        if (strtoupper($type) == 'JSON') {
            // 返回JSON数据格式到客户端 包含状态信息
            header('Content-Type:text/json; charset=utf-8');
            switch (json_last_error()) {
                case JSON_ERROR_DEPTH:
                    exit(json_encode(array('result' => 'Maximum stack depth exceeded', 'status' => 0)));
                    break;
                case JSON_ERROR_STATE_MISMATCH:
                    exit(json_encode(array('result' => 'Underflow or the modes mismatch', 'status' => 0)));
                    break;
                case JSON_ERROR_CTRL_CHAR:
                    exit(json_encode(array('result' => 'Unexpected control character found', 'status' => 0)));
                    break;
                case JSON_ERROR_SYNTAX:
                    exit(json_encode(array('result' => 'Syntax error, malformed JSON', 'status' => 0)));
                    break;
                case JSON_ERROR_UTF8:
                    exit(json_encode(array('result' => 'Malformed UTF-8 characters, possibly incorrectly encoded', 'status' => 0)));
                    break;
                case JSON_ERROR_NONE:
                default:
                    exit(think_json_encode($data));
                    break;
            }
        }
        elseif (strtoupper($type) == 'XML') {
            // 返回xml格式数据
            header('Content-Type:text/xml; charset=utf-8');
            exit(xml_encode($data));
        }
        elseif (strtoupper($type) == 'EVAL') {
            // 返回可执行的js脚本
            header('Content-Type:text/html; charset=utf-8');
            exit($data);
        }
    }

    /**
     * Action跳转(URL重定向） 支持指定模块和延时跳转
     * @access protected
     * @param string $url 跳转的URL表达式
     * @param array $params 其它URL参数
     * @param integer $delay 延时跳转的时间 单位为秒
     * @param string $msg 跳转提示信息
     * @return void
     */
    protected function redirect($url, $params = array(), $delay = 0, $msg = '')
    {
        $url = U($url, $params);
        redirect($url, $delay, $msg);
    }

    /**
     * 默认跳转操作 支持错误导向和正确跳转
     * 调用模板显示 默认为public目录下面的success页面
     * 提示页面为可配置 支持模板标签
     * @param string $message 提示信息
     * @param Boolean $status 状态
     * @param string $jumpUrl 页面跳转地址
     * @param Boolean|array $ajax 是否为Ajax方式
     * @access private
     * @return void
     */
    private function dispatchJump($message, $status = 1, $jumpUrl = '', $ajax = false)
    {
        if ($ajax || $this->isAjax()) { // AJAX提交
            $data           = is_array($ajax) ? $ajax : $this->get();
            $data['info']   = $message;
            $data['status'] = $status;
            $data['url']    = $jumpUrl;
            $this->ajaxReturn($data);
        }
        if (!empty($jumpUrl)) {
            $this->assign('jumpUrl', $jumpUrl);
        }
        // 提示标题
        $this->assign('msgTitle', $status ? L('_OPERATION_SUCCESS_') : L('_OPERATION_FAIL_'));
        //如果设置了关闭窗口，则提示完毕后自动关闭窗口
        if ($this->get('closeWin')) {
            $this->assign('jumpUrl', 'javascript:window.close();');
        }
        $this->assign('status', $status); // 状态
        //保证输出不受静态缓存影响
        C('HTML_CACHE_ON', false);
        if ($status) { //发送成功信息
            $this->assign('message', $message); // 提示信息
            // 成功操作后默认停留1秒
            if (!$this->get('waitSecond')) {
                $this->assign('waitSecond', '1');
            }
            // 默认操作成功自动返回操作前页面
            if (!$this->get('jumpUrl')) {
                $this->assign("jumpUrl", $_SERVER["HTTP_REFERER"]);
            }
            $this->display(C('TMPL_ACTION_SUCCESS'));
        }
        else {
            $this->assign('error', $message); // 提示信息
            //发生错误时候默认停留3秒
            if (!$this->get('waitSecond')) {
                $this->assign('waitSecond', '3');
            }
            // 默认发生错误的话自动返回上页
            if (empty($jumpUrl)) {
                $jumpUrl = 'javascript:history.back(-1);';
            }
            if (!$this->get('jumpUrl')) {
                $this->assign('jumpUrl', $jumpUrl);
            }
            $this->display(C('TMPL_ACTION_ERROR'));
            // 中止执行  避免出错后继续执行
            exit;
        }
    }

    /**
     * 析构方法
     * @access public
     */
    public function __destruct()
    {
        // 保存日志
        if (C('LOG_RECORD')) {
            Log::save();
        }
        // 执行后续操作
        tag('action_end');
    }
}